package cn.bluethink.eguan.utils;


import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.stereotype.Component;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;

/**
 * @author GeoDev
 */
@Component
public final class TokenManager {

    private static final String TYPE = "JWT";
    private static final String ALGORITHM = "HS256";
    private static final String ISSUER = "http://www.bluethink.cn";
    private static final long TTLMillis = 1000 * 60 * 60 * 8;

    private static ExpiringMap<Long, String> TOKEN_CACHE = null;

    static {
        TOKEN_CACHE = ExpiringMap.builder()
                .maxSize(1000000)
                .expiration(8, TimeUnit.HOURS)
                .expirationPolicy(ExpirationPolicy.ACCESSED)
                .variableExpiration()
                .build();
    }

    /**
     * 创建令牌
     * @param uid 用户Id
     * @return
     */
    public String createToken(long uid){
        String token = null;
        Map<String, Object> payload = new HashMap<>();
        payload.put("uid", uid);

        String secret = UUID.randomUUID().toString();
        long nowMillis = System.currentTimeMillis();
        long expMillis = nowMillis + TTLMillis;

        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            token = JWT.create()
                    .withClaim("typ", TYPE)
                    .withClaim("alg", ALGORITHM)
                    .withHeader(payload)
                    .withIssuer(ISSUER)
                    .withIssuedAt(new Date(nowMillis))
                    .withNotBefore(new Date(nowMillis))
                    .withExpiresAt(new Date(expMillis))
                    .sign(algorithm);
            TOKEN_CACHE.put(uid, secret);
        } catch (UnsupportedEncodingException e){
            //e.printStackTrace();
        } catch (JWTCreationException e){
            //e.printStackTrace();
        }
        return token;
    }

    /**
     * 验证令牌
     * @param token 令牌
     * @return
     */
    public long verifyToken(String token){
        String secret = getSecret(token);
        if(secret == null){
            return 0;
        }

        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer(ISSUER)
                    .acceptExpiresAt(TTLMillis / 1000)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            Claim claim = jwt.getHeaderClaim("uid");
            if(claim != null){
                return claim.asLong();
            }
        } catch (Exception e){
        	//e.printStackTrace();
        }
        return 0;
    }

    /**
     * 清除Token
     * @param token
     */
    public long removeToken(String token){
        long uid = decodedUserId(token);
        if(uid > 0){
            TOKEN_CACHE.remove(uid);
        }
        return uid;
    }

    private String getSecret(String token){
        long uid = decodedUserId(token);
        if(uid > 0){
            return TOKEN_CACHE.get(uid);
        }
        return null;
    }

    public long decodedUserId(String token) {
        if(token != null && !token.equals("")){
            try {
                DecodedJWT jwt = JWT.decode(token);
                Claim claim = jwt.getHeaderClaim("uid");
                if(claim != null){
                    return claim.asLong();
                }
            } catch (Exception e){
                //e.printStackTrace();
            }
        }
        return 0;
    }
}
